﻿
using System;
using System.Drawing;

namespace ColorTools {
    public class DevColor {
        public const int HLSMAX = 240;
        public const int RGBMAX = 255;

        public enum ColorType {
            HSL,
            RGB
        }

        private readonly int _hue;
        private readonly int _sat;
        private readonly int _lum;
        private readonly int _red;
        private readonly int _green;
        private readonly int _blue;

        private DevColor(ColorType colorType, int hueOrRed, int satOrGreen, int lumOrBlue) {
            if (colorType == ColorType.HSL) {
                _hue = hueOrRed;
                _sat = satOrGreen;
                _lum = lumOrBlue;

                if (_sat == 0) {
                    _red = _green = _blue = (_lum * RGBMAX) / HLSMAX;
                } else {
                    int magic2;

                    if (_lum <= (HLSMAX / 2)) {
                        magic2 = (_lum * (HLSMAX + _sat) + (HLSMAX / 2)) / HLSMAX;
                    } else {
                        magic2 = _lum + _sat - ((_lum * _sat) + (HLSMAX / 2)) / HLSMAX;
                    }
                    int magic1 = 2 * _lum - magic2;

                    /* get RGB, change units from HLSMAX to RGBMAX */
                    _red = (HueToRGB(magic1, magic2, _hue + (HLSMAX / 3)) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
                    _green = (HueToRGB(magic1, magic2, _hue) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
                    _blue = (HueToRGB(magic1, magic2, _hue - (HLSMAX / 3)) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
                }
            } else if (colorType == ColorType.RGB) {
                _red = hueOrRed;
                _green = satOrGreen;
                _blue = lumOrBlue;

                Color clr = Color.FromArgb(_red, _green, _blue);
                _hue = (int)Math.Floor(clr.GetHue() * HLSMAX / 360.0);
                _sat = (int)Math.Floor(clr.GetSaturation() * HLSMAX);
                _lum = (int)Math.Floor(clr.GetBrightness() * HLSMAX);
            }
        }

        public int Hue {
            get { return _hue; }
        }

        public int Sat {
            get { return _sat; }
        }

        public int Lum {
            get { return _lum; }
        }

        public int Red {
            get { return _red; }
        }

        public int Green {
            get { return _green; }
        }

        public int Blue {
            get { return _blue; }
        }

        public string HTML {
            get {
                return string.Format("#{0:X2}{1:X2}{2:X2}", Red, Green, Blue);
            }
        }

        public Color Color {
            get {
                int red = Red;
                int green = Green;
                int blue = Blue;
                if (red < 0) red = 0;
                if (red > 255) red = 255;
                if (green < 0) green = 0;
                if (green > 255) green = 255;
                if (blue < 0) blue = 0;
                if (blue > 255) blue = 255;
                return Color.FromArgb(red, green, blue);
            }
        }

        public static DevColor FromHLS(int hue, int sat, int lum) {
            return new DevColor(ColorType.HSL, hue, sat, lum);
        }

        public static DevColor FromRGB(int red, int green, int blue) {
            return new DevColor(ColorType.RGB, red, green, blue);
        }

        private static int HueToRGB(int n1, int n2, int hue) {
            if (hue < 0) {
                hue += HLSMAX;
            }

            if (hue > HLSMAX) {
                hue -= HLSMAX;
            }

            if (hue < (HLSMAX / 6)) {
                return (n1 + (((n2 - n1) * hue + (HLSMAX / 12)) / (HLSMAX / 6)));
            }
            if (hue < (HLSMAX / 2)) {
                return (n2);
            }
            if (hue < ((HLSMAX * 2) / 3)) {
                return (n1 + (((n2 - n1) * (((HLSMAX * 2) / 3) - hue) + (HLSMAX / 12)) / (HLSMAX / 6)));
            }
            return (n1);
        }
    }
}